package com.idormy.sms.forwarder.utils

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL

@Suppress("unused")
class AliyunDriveUtils(private val context: Context) {

    companion object {
        private const val TAG = "AliyunDriveUtils"
        private const val BASE_URL = "https://api.aliyundrive.com"
        private const val AUTH_URL = "https://passport.aliyundrive.com"
        
        // SharedPreferences keys
        private const val PREF_NAME = "aliyun_drive"
        private const val KEY_ACCESS_TOKEN = "access_token"
        private const val KEY_REFRESH_TOKEN = "refresh_token"
        private const val KEY_DRIVE_ID = "drive_id"
        private const val KEY_USER_ID = "user_id"
    }

    private val gson = Gson()
    private val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)

    private fun createConnection(urlString: String, method: String = "GET"): HttpURLConnection {
        val url = URL(urlString)
        val connection = url.openConnection() as HttpURLConnection
        connection.requestMethod = method
        connection.connectTimeout = 30000
        connection.readTimeout = 30000
        connection.setRequestProperty("Content-Type", "application/json")
        return connection
    }

    // Data classes for API responses
    data class QrCodeResponse(
        @SerializedName("qrCodeUrl") val qrCodeUrl: String,
        @SerializedName("sid") val sid: String,
        @SerializedName("ck") val ck: String
    )

    data class QrCodeStatusResponse(
        @SerializedName("status") val status: String,
        @SerializedName("authCode") val authCode: String?
    )

    data class TokenResponse(
        @SerializedName("access_token") val accessToken: String,
        @SerializedName("refresh_token") val refreshToken: String,
        @SerializedName("default_drive_id") val driveId: String,
        @SerializedName("user_id") val userId: String
    )

    data class UploadResponse(
        @SerializedName("file_id") val fileId: String,
        @SerializedName("upload_id") val uploadId: String?,
        @SerializedName("part_info_list") val partInfoList: List<PartInfo>?
    )

    data class PartInfo(
        @SerializedName("part_number") val partNumber: Int,
        @SerializedName("upload_url") val uploadUrl: String
    )

    /**
     * 获取登录二维码
     * @return Pair<二维码图片文件, sid>
     */
    suspend fun getLoginQrCode(): Pair<File?, String> = withContext(Dispatchers.IO) {
        try {
            // 1. 获取二维码链接
            val connection = createConnection(
                "$AUTH_URL/newlogin/qrcode/generate.do?appName=aliyun_drive&fromSite=52&appEntrance=web&isMobile=false&lang=zh_CN&returnUrl=&bizParams=&_bx-v=2.2.3"
            )
            
            val responseBody = connection.inputStream.bufferedReader().use { it.readText() }
            Log.d(TAG, "QR Code Response: $responseBody")
            
            val qrCodeData = gson.fromJson(responseBody, QrCodeResponse::class.java)
            val sid = qrCodeData.sid

            // 2. 下载二维码图片
            val qrImageConnection = createConnection(qrCodeData.qrCodeUrl)
            val qrImageBytes = qrImageConnection.inputStream.readBytes()

            val bitmap = BitmapFactory.decodeByteArray(qrImageBytes, 0, qrImageBytes.size)
            val qrFile = File(context.cacheDir, "aliyun_qr_${System.currentTimeMillis()}.png")
            FileOutputStream(qrFile).use { out ->
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
            }
            Log.d(TAG, "QR Code saved to: ${qrFile.absolutePath}")
            
            Pair(qrFile, sid)
        } catch (e: Exception) {
            Log.e(TAG, "Failed to get QR code: ${e.message}")
            e.printStackTrace()
            Pair(null, "")
        }
    }

    /**
     * 检查二维码扫描状态并获取token
     * @param sid 二维码会话ID
     * @return 是否登录成功
     */
    suspend fun checkQrCodeStatus(sid: String): Boolean = withContext(Dispatchers.IO) {
        try {
            val connection = createConnection(
                "$AUTH_URL/newlogin/qrcode/query.do?appName=aliyun_drive&fromSite=52&_bx-v=2.2.3",
                "POST"
            )
            connection.doOutput = true
            
            OutputStreamWriter(connection.outputStream).use { writer ->
                writer.write("""{"sid":"$sid"}""")
                writer.flush()
            }
            
            val responseBody = connection.inputStream.bufferedReader().use { it.readText() }
            Log.d(TAG, "QR Status Response: $responseBody")
            
            val statusData = gson.fromJson(responseBody, QrCodeStatusResponse::class.java)
            
            if (statusData.status == "CONFIRMED" && statusData.authCode != null) {
                // 获取access token
                return@withContext getAccessToken(statusData.authCode)
            }
            
            false
        } catch (e: Exception) {
            Log.e(TAG, "Failed to check QR status: ${e.message}")
            e.printStackTrace()
            false
        }
    }

    /**
     * 使用授权码获取访问令牌
     */
    private suspend fun getAccessToken(authCode: String): Boolean = withContext(Dispatchers.IO) {
        try {
            val connection = createConnection("$BASE_URL/v2/oauth/token", "POST")
            connection.doOutput = true
            
            OutputStreamWriter(connection.outputStream).use { writer ->
                writer.write("""{"code":"$authCode","grant_type":"authorization_code"}""")
                writer.flush()
            }
            
            val responseBody = connection.inputStream.bufferedReader().use { it.readText() }
            Log.d(TAG, "Token Response: $responseBody")
            
            val tokenData = gson.fromJson(responseBody, TokenResponse::class.java)
            
            // 保存token信息
            prefs.edit().apply {
                putString(KEY_ACCESS_TOKEN, tokenData.accessToken)
                putString(KEY_REFRESH_TOKEN, tokenData.refreshToken)
                putString(KEY_DRIVE_ID, tokenData.driveId)
                putString(KEY_USER_ID, tokenData.userId)
                apply()
            }
            
            Log.d(TAG, "Login successful, tokens saved")
            true
        } catch (e: Exception) {
            Log.e(TAG, "Failed to get access token: ${e.message}")
            e.printStackTrace()
            false
        }
    }

    /**
     * 刷新访问令牌
     */
    suspend fun refreshAccessToken(): Boolean = withContext(Dispatchers.IO) {
        try {
            val refreshToken = prefs.getString(KEY_REFRESH_TOKEN, "") ?: return@withContext false
            if (refreshToken.isEmpty()) return@withContext false

            val connection = createConnection("$BASE_URL/v2/oauth/token", "POST")
            connection.doOutput = true
            
            OutputStreamWriter(connection.outputStream).use { writer ->
                writer.write("""{"refresh_token":"$refreshToken","grant_type":"refresh_token"}""")
                writer.flush()
            }
            
            val responseBody = connection.inputStream.bufferedReader().use { it.readText() }
            val tokenData = gson.fromJson(responseBody, TokenResponse::class.java)
            
            prefs.edit().apply {
                putString(KEY_ACCESS_TOKEN, tokenData.accessToken)
                putString(KEY_REFRESH_TOKEN, tokenData.refreshToken)
                apply()
            }
            
            Log.d(TAG, "Token refreshed successfully")
            true
        } catch (e: Exception) {
            Log.e(TAG, "Failed to refresh token: ${e.message}")
            e.printStackTrace()
            false
        }
    }

    /**
     * 上传文件
     * @param file 要上传的文件
     * @param parentFileId 父文件夹ID，默认为root
     * @return 是否上传成功
     */
    suspend fun uploadFile(file: File, parentFileId: String = "root"): Boolean = withContext(Dispatchers.IO) {
        try {
            val accessToken = prefs.getString(KEY_ACCESS_TOKEN, "") ?: return@withContext false
            val driveId = prefs.getString(KEY_DRIVE_ID, "") ?: return@withContext false
            
            if (accessToken.isEmpty() || driveId.isEmpty()) {
                Log.e(TAG, "Not logged in")
                return@withContext false
            }

            // 1. 创建文件
            val createFileJson = """
                {
                    "drive_id": "$driveId",
                    "parent_file_id": "$parentFileId",
                    "name": "${file.name}",
                    "type": "file",
                    "check_name_mode": "auto_rename",
                    "size": ${file.length()}
                }
            """.trimIndent()

            val createConnection = createConnection("$BASE_URL/adrive/v2/file/createWithFolders", "POST")
            createConnection.setRequestProperty("Authorization", "Bearer $accessToken")
            createConnection.doOutput = true
            
            OutputStreamWriter(createConnection.outputStream).use { writer ->
                writer.write(createFileJson)
                writer.flush()
            }
            
            val createBody = createConnection.inputStream.bufferedReader().use { it.readText() }
            Log.d(TAG, "Create file response: $createBody")
            
            val uploadData = gson.fromJson(createBody, UploadResponse::class.java)
            
            // 2. 上传文件内容
            if (uploadData.partInfoList != null && uploadData.partInfoList.isNotEmpty()) {
                val uploadUrl = uploadData.partInfoList[0].uploadUrl
                val uploadConnection = URL(uploadUrl).openConnection() as HttpURLConnection
                uploadConnection.requestMethod = "PUT"
                uploadConnection.doOutput = true
                uploadConnection.setRequestProperty("Content-Type", "application/octet-stream")
                
                uploadConnection.outputStream.use { output ->
                    file.inputStream().use { input ->
                        input.copyTo(output)
                    }
                }
                
                val uploadResponseCode = uploadConnection.responseCode
                if (uploadResponseCode !in 200..299) {
                    Log.e(TAG, "Upload failed: $uploadResponseCode")
                    return@withContext false
                }
                
                // 3. 完成上传
                val completeJson = """
                    {
                        "drive_id": "$driveId",
                        "file_id": "${uploadData.fileId}",
                        "upload_id": "${uploadData.uploadId}"
                    }
                """.trimIndent()

                val completeConnection = createConnection("$BASE_URL/v2/file/complete", "POST")
                completeConnection.setRequestProperty("Authorization", "Bearer $accessToken")
                completeConnection.doOutput = true
                
                OutputStreamWriter(completeConnection.outputStream).use { writer ->
                    writer.write(completeJson)
                    writer.flush()
                }
                
                val completeResponseCode = completeConnection.responseCode
                Log.d(TAG, "Upload complete: $completeResponseCode")
                
                return@withContext completeResponseCode in 200..299
            }
            
            false
        } catch (e: Exception) {
            Log.e(TAG, "Failed to upload file: ${e.message}")
            e.printStackTrace()
            false
        }
    }

    /**
     * 上传文件夹（递归上传所有文件）
     * @param folder 要上传的文件夹
     * @param parentFileId 父文件夹ID
     * @return 是否全部上传成功
     */
    suspend fun uploadFolder(folder: File, parentFileId: String = "root"): Boolean = withContext(Dispatchers.IO) {
        try {
            if (!folder.isDirectory) {
                return@withContext uploadFile(folder, parentFileId)
            }

            val accessToken = prefs.getString(KEY_ACCESS_TOKEN, "") ?: return@withContext false
            val driveId = prefs.getString(KEY_DRIVE_ID, "") ?: return@withContext false

            // 1. 创建文件夹
            val createFolderJson = """
                {
                    "drive_id": "$driveId",
                    "parent_file_id": "$parentFileId",
                    "name": "${folder.name}",
                    "type": "folder",
                    "check_name_mode": "auto_rename"
                }
            """.trimIndent()

            val createConnection = createConnection("$BASE_URL/adrive/v2/file/createWithFolders", "POST")
            createConnection.setRequestProperty("Authorization", "Bearer $accessToken")
            createConnection.doOutput = true
            
            OutputStreamWriter(createConnection.outputStream).use { writer ->
                writer.write(createFolderJson)
                writer.flush()
            }
            
            val createBody = createConnection.inputStream.bufferedReader().use { it.readText() }
            val folderData = gson.fromJson(createBody, UploadResponse::class.java)
            val newFolderId = folderData.fileId

            // 2. 递归上传文件夹内容
            var allSuccess = true
            folder.listFiles()?.forEach { file ->
                val success = if (file.isDirectory) {
                    uploadFolder(file, newFolderId)
                } else {
                    uploadFile(file, newFolderId)
                }
                if (!success) allSuccess = false
            }

            allSuccess
        } catch (e: Exception) {
            Log.e(TAG, "Failed to upload folder: ${e.message}")
            e.printStackTrace()
            false
        }
    }

    /**
     * 检查是否已登录
     */
    fun isLoggedIn(): Boolean {
        val accessToken = prefs.getString(KEY_ACCESS_TOKEN, "")
        return !accessToken.isNullOrEmpty()
    }

    /**
     * 清除登录信息
     */
    fun logout() {
        prefs.edit().clear().apply()
        Log.d(TAG, "Logged out")
    }
}
